grammar EventAI;

options {
	language = Java;
	output = AST;	
}

tokens {
	AFTER='AFTER';
	AGGRO='AGGRO';
	ALLIANCE='ALLIANCE';
	ALWAYS='ALWAYS';
	ARCANE='ARCANE';
	AT='AT';
	ATTACK='ATTACK';
	AURA='AURA';
	AUTO='AUTO';
	ALL='ALL';
	BUFF='BUFF';
	CAST='CAST';
	CASTING='CASTING';
	CC='CC';
	CHANCE='CHANCE';
	COMBAT='COMBAT';
	COMMENT='COMMENT';
	COMPLETE='COMPLETE';
	CREATURE='CREATURE';
	DATA='DATA';
	DEATH='DEATH';
	DEBUG='DEBUG';
	DEC='DEC';
	DEFAULT='DEFAULT';
	DEFICIT='DEFICIT';
	DEFINE='DEFINE';
	DIE='DIE';
	EMOTE='EMOTE';
	END='END';
	EVADE='EVADE';
	EVENT='EVENT';
	EVERY='EVERY';
	FIELD='FIELD';
	FIRE='FIRE';
	FLAG='FLAG';
	FLEE='FLEE';
	FOR='FOR';
	FORCE='FORCE';
	FORCE_TARGET_SELF='FORCE_TARGET_SELF';
	FRIENDLY='FRIENDLY';
	FROM='FROM';
	FROST='FROST';
	GO='GO';
	HEROIC='HEROIC';
	HOLY='HOLY';
	HORDE='HORDE';
	HOSTILE = 'HOSTILE';
	HP='HP';
	INC='INC';
	INCLUDE='INCLUDE';
	INSTANCE='INSTANCE';
	INTERRUPT='INTERRUPT';
	INVOKER='INVOKER';
	IS='IS';
	KILL='KILL';
	KILLED='KILLED';
	LAST='LAST';
	LOS='LOS';
	MANA='MANA';
	MISSING='MISSING';
	MONSTER='MONSTER';
	MOVEMENT='MOVEMENT';
	NATURE='NATURE';
	NOFRIENDLY='NOFRIENDLY';
	NOHOSTILE='NOHOSTILE';
	NORMAL='NORMAL';
	NOT='NOT';
	NO_MELEE_IF_OOM='NO_MELEE_IF_OOM';
	OFF='OFF';
	ON='ON';
	OOC='OOC';
	OR='OR';
	PHASE='PHASE';
	QUEST='QUEST';
	RANDOM='RANDOM';
	RANGE='RANGE';
	RANGED='RANGED';
	REPEAT='REPEAT';
	REMOVE='REMOVE';
	SAY='SAY';
	SCHOOL='SCHOOL';
	SCRIPT='SCRIPT';
	SECOND='SECOND';
	SELF = 'SELF';
	SET='SET';
	SHADOW='SHADOW';
	SINGLE='SINGLE';
	SOUND='SOUND';
	SPAWNED='SPAWNED';
	SPELL = 'SPELL';
	SPELLHIT='SPELLHIT';
	SUMMON='SUMMON';
	SUMMONED='SUMMONED';
	TARGET='TARGET';
	TEMPLATE='TEMPLATE';
	TEXT='TEXT';
	THREAT='THREAT';
	TIMER='TIMER';
	TRIGGERED='TRIGGERED';
	TO='TO';
	TOP='TOP';
	TURN='TURN';
	UNIT='UNIT';
	UPDATE='UPDATE';
	VALUE='VALUE';
	YELL='YELL';
}

@header {
package org.eaicompiler.antlr;
import org.eaicompiler.antlr.exceptions.*;
import org.eaicompiler.model.*;
import org.eaicompiler.util.Util;
import java.io.IOException;
} 

@lexer::header {
package org.eaicompiler.antlr;
import org.eaicompiler.antlr.exceptions.*;
}

@members {
	public ScriptDefinition scriptDefinition = null;

	// We can't use generics here - ANTLR limitation :(
	public List scriptDefinitions = new ArrayList(); 
		
	public int percent = 0;
	public int time = 0;
	public int target = 0;
	public Spawn spawn = null;
	public int school = 0;

	private int unitField = 0;
	private int unitFlag = 0;
	private int instanceField = 0;
	private int eventChance = 100;
	private int emote;	
	private int sound;
	private int spell;
	private int quest;
	private int creature;
	private int template;
	
	private int repeatMin = 0;
	private int repeatMax = 0;
	
	public LocalizedText localizedText = null;
	
	public Event event = null;
	public Action action = null;		
}

@lexer::members {
  public void reportError(RecognitionException re)
  {    
    throw new ParseException(re.line, re.charPositionInLine);
  }
}

script:
	includes*
	defines*
	(
		scriptDefinition
		phase*
		{
			scriptDefinitions.add(scriptDefinition);
			scriptDefinition = null;
		}
	)* 
	;

includes
	:
		(
			INCLUDE includeFile = StringLiteral
			{
				try {
     	  	CharStream tmp_input = new ANTLRFileStream(Util.unQuoteString(includeFile.getText()));
     	  	EventAILexer tmp_lex = new EventAILexer(tmp_input);
     	  	CommonTokenStream tmp_tokens = new CommonTokenStream(tmp_lex);
     	  	EventAIParser tmp_parser = new EventAIParser(tmp_tokens);	    		
     			tmp_parser.script();
     	  } catch (IOException e) {
     			throw new RuntimeException("File not found: " + includeFile.getText()); 		
     	  }
     	}
		)
	;

defines
	:	
	(
		{
			int defineType = Defines.DEFINE_UNKNOWN;
		}
		DEFINE
		(
			(
				SPELL
				{
					defineType = Defines.DEFINE_SPELL;
				}
				|
				FLAG
				{
					defineType = Defines.DEFINE_FLAG;
				}
				|
				UNIT FIELD
				{
					defineType = Defines.DEFINE_UNIT_FIELD;
				}
				|
				INSTANCE FIELD
				{
					defineType = Defines.DEFINE_INSTANCE_FIELD;
				}
				|
				EMOTE
				{
					defineType = Defines.DEFINE_EMOTE;
				}
				|
				QUEST
				{
					defineType = Defines.DEFINE_QUEST;
				}
				|
				SOUND
				{
					defineType = Defines.DEFINE_SOUND;
				}
				|
				CREATURE
				{
					defineType = Defines.DEFINE_CREATURE;
				}
				|
				TEMPLATE
				{
					defineType = Defines.DEFINE_TEMPLATE;
				}
			)			
			defineValue = IntegerLiteral
			defineName = StringLiteral
			{
				Util.define(defineType, defineName.getText(), Integer.parseInt(defineValue.getText()));
			}
		)
	)
	;

scriptDefinition
	:	
	SCRIPT FOR CREATURE
	creatureList = integerList
	{
		scriptDefinition = new ScriptDefinition();
		scriptDefinition.setupCreatureFor(creatureList);
	}
	;
	
phase
	:
	PHASE
	(
		ALL
		{
			scriptDefinition.setPhaseAll();
		}
		| 
		phases = integerList
		{
			scriptDefinition.setPhase(phases);
		}		
	)
	event *
	;

event
	:
	{
		event = null; // Let us make the fresh event holder
		eventChance = 100;
		boolean eventNormal = false;
		boolean eventHeroic = false;
		boolean eventDebug  = false;
	}
	EVENT
	(
	  NORMAL
	  {
	    eventNormal = true;
	  }
	)?
  (
    HEROIC
    {
      eventHeroic = true;
    }
  )?
  (
    DEBUG
    {
      eventDebug = true;
    }
  )?
	(
		CHANCE
		percentLiteral
		{
			eventChance = percent;
		}
	)?
	eventType
	comment?
	action+	
	END
	{
		if(event!=null)
		{
			event.setChance(eventChance);
			if(eventNormal)
			{
			 event.setEventFlags(event.getEventFlags() | Event.EFLAG_NORMAL);
			}
      if(eventHeroic)
      {
       event.setEventFlags(event.getEventFlags() | Event.EFLAG_HEROIC);
      }
      if(eventDebug)
      {
       event.setEventFlags(event.getEventFlags() | Event.EFLAG_DEBUG);
      }
			if(scriptDefinition!=null)
			{
				scriptDefinition.addEvent(event);
				event = null;
			} else {
				throw new NoScriptDefinition("SCRIPT definition is required");
			}
		} else {
			throw new NotInEventException("END have to be ending an defined event");
		}		
	}
	;	
	
eventType
	:
	eventOnAggro
	|
	eventOnHp
	|
	eventOnTimer
	|
	eventOnKill
	|
	eventOnDeath
	|
	eventOnMana
	|
	eventOnEvade
	|
	eventOnSpellhit
	|
	eventOnRange
	|
	eventOnOocLos
	|
	eventOnSpawned
	|
	eventOnTargetHp
	|
	eventOnTargetCasting
	|
	eventOnFriendlyHp
	|
	eventOnFriendlyIsCC
	|
	eventOnMissingBuff
	|
  eventOnSummoned
	;

eventOnSummoned
  :
  (
    {
      event = new Event();
      event.setType(Event.EVENT_T_SUMMONED_UNIT);
    }
    repeatable
    ON SUMMONED
    (
	    (
	      creatureLiteral
	      {
	        event.setParam(0, creature);      
	      }
	    )
	    |
	    (
	      ALL
	      {
	        event.setParam(0, 0);      
	      }
	    )
    )
    {
      event.setParam(1, repeatMin);
      event.setParam(2, repeatMax);
    }
  )
  ;

eventOnMissingBuff
  :
  (
    {
      event = new Event();
      event.setType(Event.EVENT_T_MISSING_BUFF);
    }
    repeatable
    ON MISSING BUFF
    spellLiteral    
    RANGE
    range = IntegerLiteral
    {
      event.setParam(0, spell);
      event.setParam(1, Integer.parseInt(range.getText()));
      event.setParam(2, repeatMin);
      event.setParam(3, repeatMax);     
    }
  )
  ;
	
eventOnFriendlyIsCC
  :
  (
    {
      event = new Event();
      event.setType(Event.EVENT_T_FRIENDLY_IS_CC);
    }
    repeatable
    ON FRIENDLY IS CC
    RANGE
    range = IntegerLiteral
    {
      event.setParam(1, Integer.parseInt(range.getText()));
      event.setParam(2, repeatMin);
      event.setParam(3, repeatMax);     
    }
  )
  ;
	
eventOnFriendlyHp
	:
	(
    {
      event = new Event();
      event.setType(Event.EVENT_T_FRIENDLY_HP);
    }
    repeatable
	  ON
		FRIENDLY HP DEFICIT
		deficit = IntegerLiteral
		RANGE
		range = IntegerLiteral
		{
      event.setParam(0, Integer.parseInt(deficit.getText()));
      event.setParam(1, Integer.parseInt(range.getText()));
      event.setParam(2, repeatMin);
      event.setParam(3, repeatMax);     
		}
	)
	;

eventOnTargetCasting
	:
	(
    {
      event = new Event();
      event.setType(Event.EVENT_T_TARGET_CASTING);    
    }
    repeatable	
		ON
		TARGET CASTING
		{
      event.setParam(1, repeatMax);
		  event.setParam(0, repeatMin);
		}
	)
	;

eventOnTargetHp
	:
	(
    {
      event = new Event();
      event.setType(Event.EVENT_T_TARGET_HP);
			int hpFrom = 0;
			int hpTo = 0;
			int hpTime = 0;
		}
		repeatable
		ON
		TARGET HP
		percentLiteral
		{
			hpTo = percent; 
		}
		(
			FROM
			percentLiteral
			{
				hpFrom = percent;
			}
		)?
		{
		  event.setParam(0, hpTo);
		  event.setParam(1, hpFrom);
		  event.setParam(2, repeatMin);
		  event.setParam(3, repeatMax);
		}
	)
	;


eventOnOocLos
  :
  ( 
    {
      event = new Event();
      event.setType(Event.EVENT_T_OOC_LOS);
    }
    repeatable
    ON
    OOC LOS
    (
      NOHOSTILE
      {
        event.setParam(0, 1);
      }
    )?
    (
      NOFRIENDLY
      {
        event.setParam(1, 1);
      }
    )?
    {
      event.setParam(2, repeatMin);
      event.setParam(3, repeatMax);
    }
  )
  ;
	
eventOnSpawned
	:
	(
		ON SPAWNED
		{
			event = new Event();
			event.setType(Event.EVENT_T_SPAWNED);
		}
	)
	;
	

eventOnRange
	:
	(
		{
			event = new Event();
			event.setType(Event.EVENT_T_RANGE);
		}
		repeatable
		ON RANGE
		(
			FROM
			rangeMin = IntegerLiteral
			{
				event.setParam(1, Integer.parseInt(rangeMin.getText()));
			}		
		)?
		TO
		rangeMax = IntegerLiteral
		{
			event.setParam(0, Integer.parseInt(rangeMax.getText()));
			event.setParam(2, repeatMin);
			event.setParam(3, repeatMax);
		}
	)
	;

eventOnSpellhit
	:
	(
		{
			event = new Event();
			event.setType(Event.EVENT_T_SPELLHIT);
			spell = 0;
			school = 0;
		}
		(
		  repeatable
		  ON SPELLHIT
			(
				(
					SPELL			
					spellLiteral
				)
				|
				(
					schoolLiteral
				)			
			)?			
			{	
				event.setParam(0, spell);
				event.setParam(1, school);
				event.setParam(2, repeatMin);
        event.setParam(3, repeatMax);
			}
		)
	)
	;

	
eventOnMana
  :
  (
    {
      event = new Event();
      event.setType(Event.EVENT_T_MANA);
      int manaFrom = 0;
      int manaTo = 0;
    }
    repeatable
    ON MANA
    percentLiteral
    {
      manaTo = percent; 
    }
    (
      FROM
      percentLiteral
      {
        manaFrom = percent;
      }
    )?
    {
      event.setParam(0, manaTo);
      event.setParam(1, manaFrom);
      event.setParam(2, repeatMin);
      event.setParam(3, repeatMax);
    }
  )
  ;

eventOnEvade
	:
	(
		ON EVADE
		{
			event = new Event();
			event.setType(Event.EVENT_T_EVADE);
		}
	)
	;
	
	
eventOnDeath
	:
	(
		ON DEATH		
		{
			event = new Event();
			event.setType(Event.EVENT_T_DEATH);
		}
	)
	;
	
eventOnKill
	:
	(
		{
			event = new Event();
			event.setType(Event.EVENT_T_KILL);
		}
		repeatable
		ON KILL
		{
      event.setParam(0, repeatMin);
      event.setParam(1, repeatMax);		  
		}	 
	)
	;

eventOnTimer
	:
	(
		{
			event = new Event();
			event.setType(Event.EVENT_T_TIMER);   
		}
    repeatable  
		ON TIMER
		(
		  (
		    OOC
		    {
		      event.setType(Event.EVENT_T_TIMER_OOC);
		    }
		  )?
			(
				{
				  int initialMin = 0;
				  int initialMax = 0;
				}
				AT
				timeLiteral
				{
					initialMin = time;
					initialMax = time;
				}
				(
					TO
					timeLiteral
					{
						initialMax = time;
					}
				)?
				{
					event.setParam(0, initialMin);		
					event.setParam(1, initialMax);		
					event.setParam(2, repeatMin);
          event.setParam(3, repeatMax);
				}		
			)
		)
	)
	;

eventOnAggro
	:
	ON AGGRO
	{
		event = new Event();
		event.setType(Event.EVENT_T_AGGRO);
	}
	;

eventOnHp
  :
  (
    {
      event = new Event();
      event.setType(Event.EVENT_T_HP);
      int hpFrom = 0;
      int hpTo = 0;
    }
    repeatable
    ON HP
    percentLiteral
    {
      hpTo = percent; 
    }
    (
      FROM
      percentLiteral
      {
        hpFrom = percent;
      }
    )?
    {
      event.setParam(0, hpTo);
      event.setParam(1, hpFrom);
      event.setParam(2, repeatMin);
      event.setParam(3, repeatMax);
    }
  )
  ;

comment
	:
	COMMENT
	text = StringLiteral
	{
		if(event!=null)
		{
			event.setComment(Util.unQuoteString(text.getText()));
		} else {
			throw new NotInEventException("COMMENT need to be inside EVENT");
		}
	}
	;
	
action
	:
	{
		action = null; // Let us clear the previous action
	}
	(
		actionSay
		|
		actionYell
		|
		actionTextEmote
		|
		actionEmote
		|
		actionSetPhase
		|
		actionCast
		|
		actionSummon
		|
		actionSound
		|
		actionThreat
		|
		actionEvade
		|
		actionQuestComplete
		|
		actionFlee
		|
		actionCastCreatureGo
		|
		actionAutoAttack
		|
		actionCombatMovement
		|
		actionSetUnitField
		|
		actionSetUnitFlag
		|
		actionRemoveUnitFlag
		|
		actionRemoveAuraFromSpell
		|
		actionRangedMovement
		|
		actionKilledMonster
		|
		actionSetInstanceData
		|
		actionUpdateTemplate
		|
		actionDie
		|
		actionTurnCombatOn
	)
	{
		if(event!=null)
		{
			event.addAction(action);
		} else {
			throw new NotInEventException("Action need to be inside EVENT");
		}
	}
	;

actionTurnCombatOn
  :
  (
    TURN COMBAT ON
    {
      action = new Action();
      action.setType(Action.ACTION_T_ZONE_COMBAT_PULSE);
    }
  )
  ;

actionDie
  :
  (
    DIE
    {
      action = new Action();
      action.setType(Action.ACTION_T_DIE);
    }
  )
  ;

actionUpdateTemplate
	:
	(
		UPDATE TEMPLATE TO
		templateLiteral
		{
			action = new Action();
			action.setType(Action.ACTION_T_UPDATE_TEMPLATE);
			action.setParam(0, template);
		}
		(
			(
				ALLIANCE
				{
					action.setParam(1, 0);
				}
			)
			|
			(
				HORDE
				{
					action.setParam(1, 1);
				}
			)
		)
	)
	;

actionSetInstanceData
	:
	(
		SET INSTANCE DATA
		instanceFieldLiteral
		TO
		(
			(
				VALUE
				fieldValue=IntegerLiteral
				{
					action = new Action();
					action.setType(Action.ACTION_T_SET_INST_DATA);
					action.setParam(0, instanceField);
					action.setParam(1, Integer.parseInt(fieldValue.getText()));
				}
			)
			|
			(				
				targetCode
				{
					action = new Action();
					action.setType(Action.ACTION_T_SET_INST_DATA64);
					action.setParam(0, instanceField);
					action.setParam(1, target);
				}
			)
			)
	)
	;



actionKilledMonster
	:
	(
		KILLED MONSTER
		creatureLiteral
		ON
		targetCode
		{
				action = new Action();
				action.setType(Action.ACTION_T_KILLED_MONSTER);
				action.setParam(0, creature);
				action.setParam(1, target);		
		}		
	)
	;
	
actionRangedMovement
	:
	(
		RANGED MOVEMENT
		(
			distance=IntegerLiteral
			','
			angle=IntegerLiteral
			{
				action = new Action();
				action.setType(Action.ACTION_T_RANGED_MOVEMENT);
				action.setParam(0, Integer.parseInt(distance.getText()));
				action.setParam(1, Integer.parseInt(angle.getText()));
			}
		)
		|
		(
			RANGED MOVEMENT OFF
			{
				action = new Action();
				action.setType(Action.ACTION_T_RANGED_MOVEMENT);
				action.setParam(0, 0);
				action.setParam(1, 0);
				action.setParam(2, 0);
			}
		)
	)
	;

actionRemoveAuraFromSpell
	:
	(
		REMOVE AURA FROM SPELL spellLiteral ON targetCode
		{
			action = new Action();
			action.setType(Action.ACTION_T_REMOVEAURASFROMSPELL);
			action.setParam(0, target);
			action.setParam(1, spell);
		}
	)
	;

actionSetUnitFlag
	:
	{
		int summedUnitFlag = 0;
	}
	(
		SET UNIT FLAG
		(
			unitFlagLiteral
			{
				summedUnitFlag = unitFlag;
			}
		)
		(
			','
			unitFlagLiteral
			{
				summedUnitFlag |= unitFlag;
			}
		)*
		
		ON
		targetCode
		{
			action = new Action();
			action.setType(Action.ACTION_T_SET_UNIT_FLAG);
			action.setParam(0, summedUnitFlag);
			action.setParam(1, target);
		}
	)
	;

actionRemoveUnitFlag
	:
	{
		int summedUnitFlag = 0;
	}
	(
		REMOVE UNIT FLAG
		(
			unitFlagLiteral
			{
				summedUnitFlag = unitFlag;
			}
		)
		(
			','
			unitFlagLiteral
			{
				summedUnitFlag |= unitFlag;
			}
		)*
		
		ON
		targetCode
		{
			action = new Action();
			action.setType(Action.ACTION_T_REMOVE_UNIT_FLAG);
			action.setParam(0, summedUnitFlag);
			action.setParam(1, target);
		}
	)
	;
	
actionSetUnitField
	:
	(
		SET UNIT FIELD
		unitFieldLiteral
		TO
		fieldValue=IntegerLiteral
		ON
		targetCode
		{
			action = new Action();
			action.setType(Action.ACTION_T_SET_UNIT_FIELD);
			action.setParam(0, unitField);
			action.setParam(1, Integer.parseInt(fieldValue.getText()));
			action.setParam(2, target);
		}
	)
	;
	
actionCombatMovement
	:
	COMBAT MOVEMENT
	(
		(
			ON
			{
				action = new Action();
				action.setType(Action.ACTION_T_COMBAT_MOVEMENT);
				action.setParam(0, 1);
			}
		)
		|
		(
			OFF
			{
				action = new Action();
				action.setType(Action.ACTION_T_COMBAT_MOVEMENT);
			}
		)
	)
	;
	
actionAutoAttack
	:
	AUTO ATTACK
	(
		(
			ON
			{
				action = new Action();
				action.setType(Action.ACTION_T_AUTO_ATTACK);
				action.setParam(0, 1);
			}
		)
		|
		(
			OFF
			{
				action = new Action();
				action.setType(Action.ACTION_T_AUTO_ATTACK);
			}
		)
	)
	;
	
actionCastCreatureGo
	:
	CAST CREATURE GO questLiteral SPELL spellLiteral ON
	(	
		(
			targetCode
			{
				action = new Action();
				action.setType(Action.ACTION_T_CASTCREATUREGO);
				action.setParam(0, quest);
				action.setParam(1, spell);
				action.setParam(2, target);
			}
		)
		|
		(
			ALL
			{
				action = new Action();
				action.setType(Action.ACTION_T_CASTCREATUREGO_ALL);
				action.setParam(0, quest);
				action.setParam(1, spell);
			}
		)
	)
	;
	
actionFlee
	:
	FLEE
	{
		action = new Action();
		action.setType(Action.ACTION_T_FLEE);
	}
	;
	
	
actionQuestComplete
	:
	QUEST COMPLETE questLiteral ON
	(
		(
			targetCode
			{
				action = new Action();
				action.setType(Action.ACTION_T_QUEST_COMPLETE);
				action.setParam(0, quest);
				action.setParam(1, target);		
			}
		)
		|
		(
			ALL
			{
				action = new Action();
				action.setType(Action.ACTION_T_QUEST_COMPLETE_ALL);
				action.setParam(0, quest);
			}
		)
	)
	;
	
actionEvade
	:
	EVADE
	{
		action = new Action();
		action.setType(Action.ACTION_T_EVADE);
	}
	;
	
actionThreat
	:
	THREAT
	(
		(
			SINGLE
			percentLiteral
			targetCode
			{
				action = new Action();
				action.setType(Action.ACTION_T_THREAT_SINGLE_PCT);
				action.setParam(0, percent);
				action.setParam(1, target);		
			}
		)
		|
		(
			ALL
			percentLiteral
			{
				action = new Action();
				action.setType(Action.ACTION_T_THREAT_ALL_PCT);
				action.setParam(0, percent);
			}
		)
	)
	;
	
actionSummon
	:
	SUMMON
	creatureLiteral
	targetCode
	{
			action = new Action();
			action.setType(Action.ACTION_T_SUMMON);
			action.setParam(0, creature);
			action.setParam(1, target);		
	}
	(
		AT
		spawnLiteral
		{
			action.setType(Action.ACTION_T_SUMMON_ID);
		}
	)?
	(
		FOR
		timeLiteral
		{
			if(action.getType()==Action.ACTION_T_SUMMON_ID)
			{
				spawn.setTime(time);
				action.setParam(2, scriptDefinition.addSpawn(spawn));
			} else {
				action.setParam(2, time);
			}
		}
	)?
	;	
	
actionCast
	:
		{
			boolean interrupt = false;
			boolean triggered = false; 
			boolean forced = false;
			boolean no_melee_if_oom = false;
			boolean force_target_self = false;
		}
		(
			CAST
			(
				spellLiteral
			)
			(
				INTERRUPT
				{
					interrupt = true;
				}
			)?
			(
				TRIGGERED
				{
					triggered = true;
				}
			)?
			(
				FORCE
				{
					forced = true;
				}
			)?
			(
				NO_MELEE_IF_OOM
				{
					no_melee_if_oom = true;
				}
			)?
			(
				FORCE_TARGET_SELF
				{
					force_target_self = true;
				}
			)?
			targetCode
		)
		{
			action = new Action();
			action.setType(Action.ACTION_T_CAST);
			action.setParam(0, spell);
			action.setParam(1, target);
			int v = 0;
			if(interrupt)
			{
				v = v + 1;
			}
			if(triggered)
			{
				v = v + 2;
			}
			if(forced)
			{
				v = v + 4;
			}	
			if(no_melee_if_oom)
			{
				v = v + 8;
			}	
			if(force_target_self)
			{
				v = v + 16;
			}	
			action.setParam(2, v);
		}
	;
	
actionSetPhase
	:
	SET PHASE	
	(
		(	
			TO
			text = IntegerLiteral
			{
				action = new Action();
				action.setType(Action.ACTION_T_SET_PHASE);
				action.setParam(0, Integer.parseInt(text.getText())-1);
			}
		)	
		|		
		(
			INC
			text = IntegerLiteral
			{
				action = new Action();
				action.setType(Action.ACTION_T_INC_PHASE);
				action.setParam(0, Integer.parseInt(text.getText()));
			}
		)
		|
		(
			DEC
			text = IntegerLiteral
			{
				action = new Action();
				action.setType(Action.ACTION_T_INC_PHASE);
				action.setParam(0, -Integer.parseInt(text.getText()));
			}
		)
		|
		(
			RANDOM
			(
				(
				  TO
					phase1 = IntegerLiteral
					{
						action = new Action();
						action.setType(Action.ACTION_T_RANDOM_PHASE);
						action.setParam(0, Integer.parseInt(phase1.getText())-1);
						action.setParam(1, -1);
						action.setParam(2, -1);
					}	
					(				
						OR	  
						phase2 = IntegerLiteral
						{
							action.setParam(1, Integer.parseInt(phase2.getText())-1);
						}
						(
							OR	  
							phase3 = IntegerLiteral
							{
								action.setParam(2, Integer.parseInt(phase3.getText())-1);
							}
						)?
					)?										
				)
				|
				(
					FROM
					phaseFrom = IntegerLiteral
					TO
					phaseTo = IntegerLiteral						
					{
						action = new Action();
						action.setType(Action.ACTION_T_RANDOM_PHASE_RANGE);
						action.setParam(0, Integer.parseInt(phaseFrom.getText())-1);
						action.setParam(1, Integer.parseInt(phaseTo.getText())-1);
						action.setParam(2, 0);
					}			
				)				
			)			
		)
	)
	;
	
actionSay
	:
	SAY
	languageList
	{
		action = new Action();
		action.setType(Action.ACTION_T_SAY);
		action.setParam(0, scriptDefinition.addLocalizedLanguage(localizedText));
	}
	(
		OR
		languageList
		{
			action.setType(Action.ACTION_T_RANDOM_SAY);
			action.setParam(1, scriptDefinition.addLocalizedLanguage(localizedText));
			action.setParam(2, -1);
		}
		(
			OR
			languageList
			{
				action.setParam(2, scriptDefinition.addLocalizedLanguage(localizedText));
			}
		)?
	)?
	;

actionYell
	:
	YELL
	languageList
	{
		action = new Action();
		action.setType(Action.ACTION_T_YELL);
		action.setParam(0, scriptDefinition.addLocalizedLanguage(localizedText));
	}
	(
		OR
		languageList
		{
			action.setType(Action.ACTION_T_RANDOM_YELL);
			action.setParam(1, scriptDefinition.addLocalizedLanguage(localizedText));
			action.setParam(2, -1);			
		}
		(
			OR
			languageList
			{
				action.setParam(2, scriptDefinition.addLocalizedLanguage(localizedText));
			}
		)?
	)?
	;

actionTextEmote
	:
	TEXT EMOTE
	languageList
	{
		action = new Action();
		action.setType(Action.ACTION_T_TEXTEMOTE);
		action.setParam(0, scriptDefinition.addLocalizedLanguage(localizedText));
	}
	(
		OR
		languageList
		{
			action.setType(Action.ACTION_T_RANDOM_TEXTEMOTE);
			action.setParam(1, scriptDefinition.addLocalizedLanguage(localizedText));
			action.setParam(2, -1);			
		}
		(
			OR
			languageList
			{
				action.setParam(2, scriptDefinition.addLocalizedLanguage(localizedText));
			}
		)?
	)?	
	;

actionEmote
	:
	EMOTE
	emoteLiteral
	{
		action = new Action();
		action.setType(Action.ACTION_T_EMOTE);
		action.setParam(0, emote);
	}
	(
		OR
		emoteLiteral
		{
			action.setType(Action.ACTION_T_RANDOM_EMOTE);
			action.setParam(1, emote);
			action.setParam(2, -1);
		}
		(
			OR
			emoteLiteral
			{
				action.setParam(2, emote);
			}
		)?
	)?
	;

actionSound
	:
	SOUND
	soundLiteral
	{
		action = new Action();
		action.setType(Action.ACTION_T_SOUND);
		action.setParam(0, sound);
	}
	(
		OR
		soundLiteral
		{
			action.setType(Action.ACTION_T_RANDOM_SOUND);
			action.setParam(1, sound);
			action.setParam(2, -1);
		}
		(
			OR
			soundLiteral
			{
				action.setParam(2, sound);
			}
		)?
	)?
	;

integerList
	:	
	IntegerLiteral
	(
		','
		IntegerLiteral
	)*
	;

stringList
	:
	StringLiteral
	(
		','
		StringLiteral
	)*
	;

languageList
	:
	{
		localizedText = new LocalizedText();
		int language = LocalizedText.DEFAULT;
		lang = null;
	}
	(lang = langCode '=' )? text = StringLiteral
	{
		if(lang!=null)
		{
			language = LocalizedText.getLanguageId(((Tree)lang.getTree()).getText());
		}
		localizedText.addLocalizedText(language, Util.unQuoteString(text.getText()));
	}
	( 
		{
			language = LocalizedText.DEFAULT;
			lang = null;
		}
		',' 
		(lang = langCode '=' )?
		text = StringLiteral	
		{
			if(lang!=null)
			{
				language = LocalizedText.getLanguageId(((Tree)lang.getTree()).getText());
			}
			localizedText.addLocalizedText(language, Util.unQuoteString(text.getText()));
		}	 
	)*
	;

targetCode
	:
	TARGET
	(
		(
			SELF
			{
				target = Target.TARGET_T_SELF;				
			}
		)
		|
		(
			HOSTILE
			{
				target = Target.TARGET_T_HOSTILE;									
			}
			(
				(
					LAST AGGRO
					{
						target = Target.TARGET_T_HOSTILE_LAST_AGGRO;									
					}
				)
				|
				(
					RANDOM
					{
						target = Target.TARGET_T_HOSTILE_RANDOM;									
					}
					(
						NOT TOP
						{
							target = Target.TARGET_T_HOSTILE_RANDOM_NOT_TOP;									
						}
					)?
				)
				|
				(
					SECOND AGGRO
					{
						target = Target.TARGET_T_HOSTILE_SECOND_AGGRO;									
					}
				)
			)?
		)
		|
		(
			INVOKER
			{
				target = Target.TARGET_T_INVOKER;			
			}
		)
	)
	;

langCode
	:
	DEFAULT
	;

repeatable
  :    
    (
      {
        repeatMin = 0;
        repeatMax = 0;        
      }
      (
	      REPEAT	      
	      ( 
	        (
  	        ALWAYS
		        {
              event.setEventFlags(event.getEventFlags() | Event.EFLAG_REPEATABLE);
  	        }
	        )
	        |
	        (
			      EVERY
			      timeLiteral
			      {
			        repeatMin = time;
			        repeatMax = time;
			      }
			      (
			        TO 
			        timeLiteral
			        {
			          repeatMax = time;
			        }
			      )?
		      )
		      {
		        event.setEventFlags(event.getEventFlags() | Event.EFLAG_REPEATABLE);
		      }
	      ) 
	    )
    )?
  ;

percentLiteral
	:
	percentString = IntegerLiteral
	'%'
	{
		percent = Integer.parseInt(percentString.getText());
		Util.validatePercent(percent);
	}
	;
 
timeLiteral
	:
	timeString = IntegerLiteral
	timeSuffix = TimeSuffix
	{
		time = Util.makeTime(Integer.parseInt(timeString.getText()), timeSuffix.getText());
	}
	;

creatureLiteral
	:
	(
		creatureString = StringLiteral
		|
		creatureString = IntegerLiteral
	)
	{
		creature = Util.resolve(Defines.DEFINE_CREATURE, creatureString.getText());
	}
	;



soundLiteral
	:
	(
		soundString = StringLiteral
		|
		soundString = IntegerLiteral
	)
	{
		sound = Util.resolve(Defines.DEFINE_SOUND, soundString.getText());
	}
	;


spellLiteral
	:
	(
		spellString = StringLiteral
		|
		spellString = IntegerLiteral
	)
	{
		spell = Util.resolve(Defines.DEFINE_SPELL, spellString.getText());
	}
	;

schoolLiteral
	:
		SCHOOL
		{
			school = 0;
		}
		(
			(
				NORMAL
				{
					school = school | SpellSchool.NORMAL;
				}
			)
			|
			(
				HOLY
				{
					school = school | SpellSchool.HOLY;
				}
			)
			|
			(
				FIRE
				{
					school = school | SpellSchool.FIRE;
				}
			)
			|
			(
				NATURE
				{
					school = school | SpellSchool.NATURE;
				}
			)
			|
			(
				FROST
				{
					school = school | SpellSchool.FROST;
				}
			)
			|
			(
				SHADOW
				{
					school = school | SpellSchool.SHADOW;
				}
			)
			|	
			(
				ARCANE
				{
					school = school | SpellSchool.ARCANE;
				}
			)
		) *
	;

questLiteral
	:
	(
		questString = StringLiteral
		|
		questString = IntegerLiteral
	)
	{
		quest = Util.resolve(Defines.DEFINE_QUEST, questString.getText());
	}
	;


emoteLiteral
	:
	(
		emoteString = StringLiteral
		|
		emoteString = IntegerLiteral
	)
	{
		emote = Util.resolve(Defines.DEFINE_EMOTE, emoteString.getText());
	}
	;


unitFlagLiteral
	:
	(
		unitFlagString = StringLiteral
		|
		unitFlagString = IntegerLiteral
	)
	{
		unitFlag = Util.resolve(Defines.DEFINE_FLAG, unitFlagString.getText());
	}
	;
	
unitFieldLiteral
	:
	(
		unitFieldString= StringLiteral
		|
		unitFieldString = IntegerLiteral
	)
	{
		unitField = Util.resolve(Defines.DEFINE_UNIT_FIELD, unitFieldString.getText());
	}
	;

instanceFieldLiteral
	:
	(
		instanceFieldString= StringLiteral
		|
		instanceFieldString = IntegerLiteral
	)
	{
		instanceField = Util.resolve(Defines.DEFINE_INSTANCE_FIELD, instanceFieldString.getText());
	}
	;

templateLiteral
	:
	(
		templateString= StringLiteral
		|
		templateString = IntegerLiteral
	)
	{
		template = Util.resolve(Defines.DEFINE_TEMPLATE, templateString.getText());
	}
	;
	
spawnLiteral
	:
	(
		spawnX = numberLiteral
		','
		spawnY = numberLiteral
		','
		spawnZ = numberLiteral
		','
		spawnO = numberLiteral
	)
	{
		spawn = Util.spawn(spawnX, spawnY, spawnZ, spawnO);
	}
	;
	
numberLiteral:
	(FloatLiteral | IntegerLiteral);
	 
TimeSuffix: 's' | 'ms';
FloatLiteral: ('-')? ('0'..'9')+ '.' ('0'..'9')*
		|	'.' ('0'..'9')+ 
		; 
IntegerLiteral: '0' | '-'? ('1'..'9' '0'..'9'*);
StringLiteral
    :  '"' ( EscapeSequence | ~('\\'|'"') )* '"'
    ;
fragment
EscapeSequence
    :   '\\' ('\"')
    ;
SingleLineComment: '//' (~('\n'|'\r'))* ('\n'|'\r'('\n')?)? {$channel=HIDDEN;};
MultiLineComment: '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;};
Whitespace: (' ' | '\t' | '\f' | '\n' | '\r')+ {$channel=HIDDEN;};